package main

import  (
	flag "github.com/spf13/pflag"
    "fmt"
    "os"
    "io"
    "bufio"
    "os/exec"
)

type selpg_args struct {
	start_page int
	end_page int
	page_len int
	page_type bool
	in_filename string
	print_dest string
}
const INT_MAX = int(^uint(0) >> 1) //system max integer
var progname string				   //for error display

func main() {

    var sa selpg_args
	process_args(&sa)			   //parse the args
    check_args(sa)				   //check the args
    process_input(sa)			   //process the input

}

func process_args(sa *selpg_args){

    progname = os.Args[0]
    
    flag.IntVarP(&sa.start_page, "start_page", "s", -1, "start page")
    flag.IntVarP(&sa.end_page, "end_page", "e", -1, "end page")
    flag.IntVarP(&sa.page_len, "page_len", "l", 72, "page length")
    flag.BoolVarP(&sa.page_type, "page_type", "f", false, "page type, false for lines delimited, true for form-feed delimited")
    flag.StringVarP(&sa.print_dest, "print_dest", "d", "", "print destination")
    flag.Parse()
    if flag.NArg() == 1 {
        sa.in_filename = flag.Args()[0]
    }
}

func check_args(sa selpg_args) {
    /*check os args*/
    if len(os.Args) < 3 {
        fmt.Fprintf(os.Stderr, "%s: not enough arguments\n", progname)
        usage()
        os.Exit(1)
	}
	if os.Args[1][0] != '-' || os.Args[1][1] != 's' {
		fmt.Fprintf(os.Stderr, "%s: 1st arg should be -s start_page\n", progname)
		usage()
		os.Exit(2)
	}
	if os.Args[2][0] != '-' || os.Args[2][1] != 'e' {
		fmt.Fprintf(os.Stderr, "%s: 2nd arg should be -e end_page\n", progname)
		usage()
		os.Exit(2)
    }
    /*check selpg args*/
    if sa.start_page < 1 || sa.start_page > INT_MAX {
        fmt.Fprintf(os.Stderr, "%s: invalid start page %d\n", progname, sa.start_page)
        usage()
        os.Exit(3)
    }
    if sa.end_page < 1 || sa.end_page > INT_MAX || sa.end_page < sa.start_page {
        fmt.Fprintf(os.Stderr, "%s: invalid end page %d\n", progname, sa.end_page)
        usage()
        os.Exit(4)
    }
    if sa.page_len < 1 || sa.page_len > INT_MAX {
        fmt.Fprintf(os.Stderr, "%s: invalid page length %d\n", progname, sa.page_len)
        usage()
        os.Exit(5)
    }
    if sa.in_filename != "" {
        if _, err := os.Stat(sa.in_filename); os.IsNotExist(err) {
            fmt.Fprintf(os.Stderr, "%s: input file \"%s\" does not exist\n", progname, sa.in_filename)
			os.Exit(6);
        }
    }

}
func usage() {
	fmt.Fprintf(os.Stderr, 
		"USAGE: %s -s start_page -e end_page" +
        " [ -f | -l lines_per_page ] [ -d dest ] [ in_filename ]\n" +
        "help:\n" +
        "-s start_page           set the start page\n" +
        "-e end_page             set the end page\n" +
        "-f                      choose form-seed delimited\n" +
        "-l  lines_per_page      choose line delimited, lines number default is 72\n" +
        "-d  dest                set the output destination\n" +
        "in_filename             set the input file\n", progname)
}

func process_input(sa selpg_args) {
    /*read setting*/
    var bf_read *bufio.Reader
    if sa.in_filename == "" {
        bf_read = bufio.NewReader(os.Stdin)
	} else {
        fin, err := os.Open(sa.in_filename)
        if err != nil {
            fmt.Fprintf(os.Stderr, "%s: could not open input file \"%s\"\n", progname, sa.in_filename)
            os.Exit(7) 
        }
        bf_read = bufio.NewReader(fin)
        defer fin.Close()
    }
    
	/*write setting*/
    var bf_write io.WriteCloser 
    if sa.print_dest == "" {
		bf_write = os.Stdout
	} else {
		/* Use "cat" as a printer */
		cmd := exec.Command("cat")
		var out_err error
		cmd.Stdout, out_err = os.OpenFile(sa.print_dest, os.O_WRONLY|os.O_TRUNC, 0600)
		if out_err != nil {
			fmt.Fprintf(os.Stderr, "%s: file not exist\n%s\n", progname, sa.print_dest)
			os.Exit(8)
		}
		bf_write, _ = cmd.StdinPipe()
		cmd.Start()
	}

    line_ctr, page_ctr, page_len := 1, 1, sa.page_len
    type_flg := '\n'
    /*default is line delimited. if page type is true, set the type_flg to form-feed delimited*/
    if sa.page_type {
        type_flg = '\f'
        page_len = 1
    }

    for {
        break_flg, crc := bf_read.ReadString(byte(type_flg));
        if crc != nil && len(break_flg) == 0 {
            break
        }
        if line_ctr > page_len {
            page_ctr++
            line_ctr = 1
        }
        /*locate in correct page, start writing*/
        if page_ctr >= sa.start_page && page_ctr <= sa.end_page {
            _, err := bf_write.Write([]byte(break_flg))
            if err != nil {
                fmt.Println(err)
                os.Exit(9)
            }
        }
        
        if sa.page_type {
            page_ctr++  //form-seed delimited
        } else {
            line_ctr++  //line delimited
        }
    }
    /*check output*/
    if page_ctr < sa.start_page {
        fmt.Fprintf(os.Stderr,"\n%s: start_page (%d) greater than total pages (%d),"+
                " no output written\n", progname, sa.start_page, page_ctr)
    } else if page_ctr < sa.end_page {
        fmt.Fprintf(os.Stderr, "\n%s: end_page (%d) greater than total pages (%d),"+
            " less output than expected\n", progname, sa.end_page, page_ctr)
    }


}
